﻿using System;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using Interactive.Utility.Extension;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Linq;

namespace Interactive.Utility.Serialize
{
    public static class Helper
    {
        #region 方式一,少了那个字节,并且更加高效

        /// <summary>
        /// 序列化
        /// </summary>
        /// <typeparam name="T">要序列化的类型</typeparam>
        /// <param name="graph">要序列化的对象</param>
        /// <returns>byte数组</returns>
        public static byte[] Serialize<T>(T graph)
        {
            if (graph is IConvertible)
            {
                var convertible = graph as IConvertible;
                switch (convertible.GetTypeCode())
                {
                    case TypeCode.String:
                        return Encoding.UTF8.GetBytes(convertible.ToString());
                    case TypeCode.Char:
                        return BitConverter.GetBytes(convertible.ToChar(CultureInfo.InvariantCulture));
                    case TypeCode.Boolean:
                        return BitConverter.GetBytes(convertible.ToBoolean(CultureInfo.InvariantCulture));
                    case TypeCode.SByte:
                        return BitConverter.GetBytes(convertible.ToSByte(CultureInfo.InvariantCulture));
                    case TypeCode.Int16:
                        return BitConverter.GetBytes(convertible.ToInt16(CultureInfo.InvariantCulture));
                    case TypeCode.UInt16:
                        return BitConverter.GetBytes(convertible.ToUInt16(CultureInfo.InvariantCulture));
                    case TypeCode.Int32:
                        return BitConverter.GetBytes(convertible.ToInt32(CultureInfo.InvariantCulture));
                    case TypeCode.Byte:
                        return new[] {convertible.ToByte(CultureInfo.InvariantCulture)};
                    case TypeCode.UInt32:
                        return BitConverter.GetBytes(convertible.ToUInt32(CultureInfo.InvariantCulture));
                    case TypeCode.Int64:
                        return BitConverter.GetBytes(convertible.ToInt64(CultureInfo.InvariantCulture));
                    case TypeCode.UInt64:
                        return BitConverter.GetBytes(convertible.ToUInt64(CultureInfo.InvariantCulture));
                    case TypeCode.Single:
                        return BitConverter.GetBytes(convertible.ToSingle(CultureInfo.InvariantCulture));
                    case TypeCode.Double:
                        return BitConverter.GetBytes(convertible.ToDouble(CultureInfo.InvariantCulture));
                    case TypeCode.DateTime:
                        return BitConverter.GetBytes(convertible.ToDateTime(CultureInfo.InvariantCulture).Ticks);
                    case TypeCode.Decimal:
                        return Encoding.UTF8.GetBytes(convertible.ToString());
                    case TypeCode.DBNull:
                        return new[] {(byte) 0};
                    default:
                        return SerializeJosn(graph);
                }
            }
            return SerializeJosn(graph);
        }


        /// <summary>
        /// 序列话
        /// </summary>
        /// <param name="value">要序列化的对象</param>
        /// <returns>byte数组</returns>
        public static byte[] SerializeJosn(object value)
        {
                 /* using (var ms = new MemoryStream())
            using (var writer = new BsonWriter(ms)){
                var serializer = new JsonSerializer();
                serializer.Serialize(writer, value);
                return ms.ToArray();
            }*/
            return Encoding.UTF8.GetBytes(SerializeString(value));
        }

        /// <summary>
        /// 得到序列化的字符串
        /// </summary>
        /// <param name="value">要序列化的对象</param>
        /// <returns></returns>
        public static string SerializeString(this object value)
        {
            return JsonConvert.SerializeObject(value, Formatting.None);
        }

        #region 性能比较高的转换方法

        /// <summary>
        /// 得到整形
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static int AsInt(this byte[] data)
        {
            return BitConverter.ToInt32(data, 0);
        }

        /// <summary>
        /// 得到整形
        /// </summary>
        /// <param name="data"></param>
        /// <param name="beginIndex"></param>
        /// <returns></returns>
        public static int AsInt(this byte[] data, int beginIndex)
        {
            return BitConverter.ToInt32(data, beginIndex);
        }


        public static string AsStr(this byte[] data)
        {
            return Encoding.UTF8.GetString(data);
        }

        public static string AsStr(this byte[] data, int index)
        {
            return Encoding.UTF8.GetString(data, index, data.Length - index);
        }

        public static string AsStr(this byte[] data, int index, int count)
        {
            return Encoding.UTF8.GetString(data, index, count);
        }

        public static bool AsBool(this byte[] data)
        {
            return BitConverter.ToBoolean(data, 0);
        }

        public static bool AsBool(this byte[] data, int index)
        {
            return BitConverter.ToBoolean(data, index);
        }

        public static Int16 AsInt16(this byte[] data)
        {
            return BitConverter.ToInt16(data, 0);
        }

        public static Int16 AsInt16(this byte[] data, int index)
        {
            return BitConverter.ToInt16(data, index);
        }

        public static Int64 AsInt64(this byte[] data)
        {
            return BitConverter.ToInt64(data, 0);
        }

        public static Int64 AsInt64(this byte[] data, int index)
        {
            return BitConverter.ToInt64(data, index);
        }


        public static Single AsSingle(this byte[] data)
        {
            return BitConverter.ToSingle(data, 0);
        }

        public static Single AsSingle(this byte[] data, int index)
        {
            return BitConverter.ToSingle(data, index);
        }


        public static Double AsDouble(this byte[] data)
        {
            return BitConverter.ToDouble(data, 0);
        }

        public static Double AsDouble(this byte[] data, int index)
        {
            return BitConverter.ToDouble(data, index);
        }

        public static DateTime AsDateTime(this byte[] data)
        {
            return DateTime.MinValue.AddTicks(BitConverter.ToInt64(data, 0));
        }

        public static DateTime AsDateTime(this byte[] data, int index)
        {
            return DateTime.MinValue.AddTicks(BitConverter.ToInt64(data, index));
        }

        #endregion

        #region 反序列化相关

        /// <summary>
        /// 反序列化,更加高效,优雅,但是对一般值类型的支持不够好,因为要进行
        /// 装箱和拆箱操作,对于一般值类型,如int,建议用AsInt等扩展方法
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="data">要反序列化的Byte数组</param>
        /// <returns>T</returns>
        public static T Deserialize<T>(this byte[] data)
        {
            return Deserialize<T>(data, 0, data.Length);
        }

        /// <summary>
        /// 反序列化,更加高效,优雅,但是对一般值类型的支持不够好,因为要进行
        /// 装箱和拆箱操作,对于一般值类型,如int,建议用AsInt等扩展方法
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="data">要反序列化的Byte数组</param>
        /// <param name="index">开始位置</param>
        /// <returns>T</returns>
        public static T Deserialize<T>(this byte[] data, int index)
        {
            return Deserialize<T>(data, index, data.Length - index);
        }

        /// <summary>
        /// 反序列化,更加高效,优雅,但是对一般值类型的支持不够好,因为要进行
        /// 装箱和拆箱操作,对于一般值类型,如int,建议用AsInt等扩展方法
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="data">要反序列化的Byte数组</param>
        /// <param name="index">开始位置</param>
        /// <param name="count">数量</param>
        /// <returns>T</returns>
        public static T Deserialize<T>(this byte[] data, int index, int count)
        {
            Type typeOfT = typeof (T);
            TypeCode typeCode = Type.GetTypeCode(typeOfT);
            switch (typeCode)
            {
                case TypeCode.String:
                    return (T) Convert.ChangeType(Encoding.UTF8.GetString(data, index, count), typeCode, null);
                case TypeCode.Char:
                    return (T) Convert.ChangeType(BitConverter.ToChar(data, index), typeCode, null);
                case TypeCode.Boolean:
                    return (T) Convert.ChangeType(BitConverter.ToBoolean(data, index), typeCode, null);
                case TypeCode.SByte:
                    return (T) Convert.ChangeType(BitConverter.ToInt16(data, index), typeCode, null);
                case TypeCode.Int16:
                    return (T) Convert.ChangeType(BitConverter.ToInt16(data, index), typeCode, null);
                case TypeCode.UInt16:
                    return (T) Convert.ChangeType(BitConverter.ToUInt16(data, index), typeCode, null);
                case TypeCode.Int32:
                    return (T) Convert.ChangeType(BitConverter.ToInt32(data, index), typeCode, null);
                case TypeCode.Byte:
                    return (T) Convert.ChangeType(data[index], typeCode, null);
                case TypeCode.UInt32:
                    return (T) Convert.ChangeType(BitConverter.ToUInt32(data, index), typeCode, null);
                case TypeCode.Int64:
                    return (T) Convert.ChangeType(BitConverter.ToInt64(data, index), typeCode, null);
                case TypeCode.UInt64:
                    return (T) Convert.ChangeType(BitConverter.ToUInt64(data, index), typeCode, null);
                case TypeCode.Single:
                    return (T) Convert.ChangeType(BitConverter.ToSingle(data, index), typeCode, null);
                case TypeCode.Double:
                    return (T) Convert.ChangeType(BitConverter.ToDouble(data, index), typeCode, null);
                case TypeCode.DateTime:
                    return
                        (T)
                        Convert.ChangeType(DateTime.MinValue.AddTicks(BitConverter.ToInt64(data, index)), typeCode, null);
                case TypeCode.Decimal:
                    return (T) Convert.ChangeType(Encoding.UTF8.GetString(data, index, count), typeCode, null);
                case TypeCode.DBNull:
                    return (T) (object) DBNull.Value;
                default:
                    return DeserializeJosn<T>(data, typeOfT, index, count);
            }
        }


        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="bson">byte数组</param>
        /// <param name="typeOfT">反序列化的类型</param>
        /// <param name="index">开始位置</param>
        /// <param name="count">数量</param>
        /// <returns>T</returns>
        public static T DeserializeJosn<T>(this byte[] bson, Type typeOfT, int index, int count)
        {
            /*using (var ms = new MemoryStream(bson, index, count))
            using (var bsonReader = new BsonReader(ms,true,DateTimeKind.Local))
            {
                var serializer = new JsonSerializer
                {
                    ObjectCreationHandling = ObjectCreationHandling.Replace,
                    MissingMemberHandling = MissingMemberHandling.Ignore,
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                };

                return (T)serializer.Deserialize(bsonReader, typeOfT);
            }*/
            return JsonConvert.DeserializeObject<T>(Encoding.UTF8.GetString(bson, index, count));
        }

        /// <summary>
        /// 将一个字符串反序列成特定类型
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="text">要反序列化的字符串</param>
        /// <returns></returns>
        public static T DeserializeString<T>(this string text)
        {
            return JsonConvert.DeserializeObject<T>(text);
        }

        /// <summary>
        /// 将一个字符串反序列化为JObject
        /// </summary>
        /// <param name="text">要反序列化的字符串</param>
        /// <returns></returns>
        public static JObject DeserializeString(this string text)
        {
            return JObject.Parse(text);
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="bson">byte数组</param>
        /// <param name="typeOfT">反序列化的类型</param>
        /// <param name="index">开始位置</param>
        public static T DeserializeJosn<T>(this byte[] bson, Type typeOfT, int index)
        {
            return DeserializeJosn<T>(bson, typeOfT, index, bson.Length - index);
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="bson">byte数组</param>
        /// <param name="typeOfT">反序列化的类型</param>
        public static T DeserializeJosn<T>(this byte[] bson, Type typeOfT)
        {
            return DeserializeJosn<T>(bson, typeOfT, 0, bson.Length);
        }


        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="bson">byte数组</param>
        /// <returns>T</returns>
        public static T DeserializeJosn<T>(this byte[] bson)
        {
            Type type = typeof (T);
            return DeserializeJosn<T>(bson, type, 0, bson.Length);
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="bson">byte数组</param>
        /// <param name="index">起始位置</param>
        public static T DeserializeJosn<T>(this byte[] bson, int index)
        {
            Type type = typeof (T);
            return DeserializeJosn<T>(bson, type, index, bson.Length - index);
        }

        /// <summary>
        /// 反序列化
        /// </summary>
        /// <typeparam name="T">要反序列化的类型</typeparam>
        /// <param name="bson">byte数组</param>
        /// <param name="index">起始位置</param>
        /// <param name="count">数量</param>
        /// <returns>结果</returns>
        public static T DeserializeJosn<T>(this byte[] bson, int index, int count)
        {
            Type type = typeof (T);
            return DeserializeJosn<T>(bson, type, index, count);
        }

        #endregion

        #endregion

        #region 方式二,多了一个byte来表示数据类型

        /// <summary>
        /// 序列化，并返回缓冲数组。
        /// </summary>
        /// <param name="graph">待序列化的对象</param>
        public static byte[] Serialize(object graph)
        {
            byte[] bytes;
            SerializedType type;

            //判断基础类型，使用BitConverter转换为byte[]
            if (graph is int)
            {
                bytes = BitConverter.GetBytes((int) graph);
                type = SerializedType.Int;
            }
            else if (graph is string)
            {
                //字符串优化
                bytes = Encoding.UTF8.GetBytes((string) graph);
                type = SerializedType.String;
            }
            else if (graph is byte[])
            {
                //数组类型优化
                bytes = (byte[]) graph;
                type = SerializedType.ByteArray;
            }
            else if (graph is DateTime)
            {
                bytes = BitConverter.GetBytes(((DateTime) graph).Ticks);
                type = SerializedType.Datetime;
            }
            else if (graph is bool)
            {
                bytes = new[] {(byte) ((bool) graph ? 1 : 0)};
                type = SerializedType.Bool;
            }
            else if (graph is byte)
            {
                bytes = new[] {(byte) graph};
                type = SerializedType.Byte;
            }
            else if (graph is short)
            {
                bytes = BitConverter.GetBytes((short) graph);
                type = SerializedType.Short;
            }
            else if (graph is ushort)
            {
                bytes = BitConverter.GetBytes((ushort) graph);
                type = SerializedType.UShort;
            }
            else if (graph is uint)
            {
                bytes = BitConverter.GetBytes((uint) graph);
                type = SerializedType.UInt;
            }
            else if (graph is long)
            {
                bytes = BitConverter.GetBytes((long) graph);
                type = SerializedType.Long;
            }
            else if (graph is ulong)
            {
                bytes = BitConverter.GetBytes((ulong) graph);
                type = SerializedType.ULong;
            }
            else if (graph is float)
            {
                bytes = BitConverter.GetBytes((float) graph);
                type = SerializedType.Float;
            }
            else if (graph is double)
            {
                bytes = BitConverter.GetBytes((double) graph);
                type = SerializedType.Double;
            }
            else
            {
                //非基本类型使用BinaryFormatter序列化
                using (var ms = new MemoryStream())
                {
                    new BinaryFormatter().Serialize(ms, graph);
                    bytes = ms.GetBuffer();
                }
                type = SerializedType.Object;
            }
            int count = bytes.Length;
            var newBytes = new byte[count + 1];
            newBytes[0] = (byte) type;
            Buffer.BlockCopy(bytes, 0, newBytes, 1, count);
            return newBytes;
        }

        /// <summary>
        /// 反序列化，并返回自身
        /// </summary>
        /// <param name="data">源字节数组</param>
        /// <returns></returns>
        /// <exception value="未定义类型声明"  cref="Exception"></exception>
        public static object Deserialize(byte[] data)
        {
            switch ((SerializedType) data[0])
            {
                case SerializedType.ByteArray:
                    int dataLength = data.Length - 1;
                    var bytes = new byte[dataLength];
                    Buffer.BlockCopy(data, 1, bytes, 0, dataLength);
                    return bytes;
                case SerializedType.String:
                    return Encoding.UTF8.GetString(data, 1, data.Length - 1);
                case SerializedType.Datetime:
                    return new DateTime(BitConverter.ToInt64(data, 1));
                case SerializedType.Bool:
                    return data[1] == 1;
                case SerializedType.Byte:
                    return data[1];
                case SerializedType.Short:
                    return BitConverter.ToInt16(data, 1);
                case SerializedType.UShort:
                    return BitConverter.ToUInt16(data, 1);
                case SerializedType.Int:
                    return BitConverter.ToInt32(data, 1);
                case SerializedType.UInt:
                    return BitConverter.ToUInt32(data, 1);
                case SerializedType.Long:
                    return BitConverter.ToInt64(data, 1);
                case SerializedType.ULong:
                    return BitConverter.ToUInt64(data, 1);
                case SerializedType.Float:
                    return BitConverter.ToSingle(data, 1);
                case SerializedType.Double:
                    return BitConverter.ToDouble(data, 1);
                case SerializedType.Object:
                    using (var ms = new MemoryStream(data))
                    {
                        ms.Position = 1;
                        object obj = new BinaryFormatter().Deserialize(ms);
                        return obj;
                    }
            }
            throw new Exception("未定义类型声明为" + (SerializedType) data[0] + "的数据转换。消息全文为：" + data.ConcatEnumerable(","));
        }

        private enum SerializedType : byte
        {
            ByteArray = 0,
            Object = 1,
            String = 2,
            Datetime = 3,
            Bool = 4,
            Byte = 6,
            Short = 7,
            UShort = 8,
            Int = 9,
            UInt = 10,
            Long = 11,
            ULong = 12,
            Float = 13,
            Double = 14,
        }

        #endregion
    }
}